<template>
  <div class="image-lazy-container" :style="containerStyle">
    <img
      v-if="showImage"
      :src="src"
      :alt="alt"
      :class="className"
      :style="imgStyle"
      @load="onLoad"
      @error="onError"
    />
    <!-- 加载中的占位图 -->
    <div v-else-if="!error" class="image-loading">
      <div class="loading-spinner"></div>
    </div>
    <!-- 加载失败的占位图 -->
    <div v-else class="image-error">
      <i class="el-icon-picture-outline"></i>
      <span>图片加载失败</span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue'

interface ImageLazyProps {
  src: string
  alt?: string
  className?: string
  width?: string | number
  height?: string | number
  placeholderSrc?: string
  errorSrc?: string
  loading?: boolean
}

const props = withDefaults(defineProps<ImageLazyProps>(), {
  alt: '',
  className: '',
  width: 'auto',
  height: 'auto',
  placeholderSrc: '',
  errorSrc: '',
  loading: true
})

const showImage = ref(false)
const error = ref(false)
const observer = ref<IntersectionObserver | null>(null)
const imgRef = ref<HTMLDivElement | null>(null)

// 计算容器样式
const containerStyle = computed(() => ({
  width: props.width,
  height: props.height,
  position: 'relative'
}))

// 计算图片样式
const imgStyle = computed(() => ({
  width: '100%',
  height: '100%',
  objectFit: 'cover'
}))

// 图片加载成功
const onLoad = () => {
  showImage.value = true
}

// 图片加载失败
const onError = () => {
  error.value = true
}

// 检查元素是否在视口中
const checkInView = () => {
  if ('IntersectionObserver' in window && imgRef.value) {
    observer.value = new IntersectionObserver((entries) => {
      const entry = entries[0]
      if (entry.isIntersecting) {
        // 元素进入视口，开始加载图片
        showImage.value = true
        // 停止观察
        observer.value?.unobserve(entry.target)
      }
    }, {
      rootMargin: '100px 0px'
    })
    
    observer.value.observe(imgRef.value)
  } else {
    // 降级处理，直接加载图片
    showImage.value = true
  }
}

onMounted(() => {
  if (props.loading) {
    checkInView()
  } else {
    // 如果不需要懒加载，直接显示图片
    showImage.value = true
  }
})

onUnmounted(() => {
  if (observer.value) {
    observer.value.disconnect()
  }
})
</script>

<style scoped lang="less">
.image-lazy-container {
  overflow: hidden;
  border-radius: 4px;
  background-color: #f5f7fa;

  .image-loading,
  .image-error {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: #f5f7fa;
    color: #909399;
  }

  .loading-spinner {
    width: 30px;
    height: 30px;
    border: 3px solid #e0e0e0;
    border-top-color: #409eff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
  }

  .image-error {
    font-size: 12px;
    i {
      font-size: 30px;
      margin-bottom: 8px;
    }
  }
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
</style>